﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using DBO.Utility;
using Serveur.DataAccess;
using DBO;

namespace BusinessManagement
{
    /// <summary>
    /// Contains static session informations for all users of the service.
    /// </summary>
    public static class SessionContainer
    {
        /// <summary>
        /// Lifetime of a session before timeout.
        /// </summary>
        public const long SessionLifetime = 6000000000;

        /// <summary>
        /// Contains all sessions, indexed with their session token.
        /// </summary>
        public static Dictionary<string, Session> SessionRepository
        {
            get
            {
                Dictionary<string, Session> res = (Dictionary<string, Session>)AppDomain.CurrentDomain.GetData("SessionDict");

                if (res == null)
                {
                    res = new Dictionary<string, Session>();
                    AppDomain.CurrentDomain.SetData("SessionDict", res);
                }

                return res;
            }
        }

        public static Player GetPlayerInfo(string token)
        {
            Dictionary<string, Session> dict = (Dictionary<string, Session>)AppDomain.CurrentDomain.GetData("SessionDict");
            return new Player()
            {
                Login = dict[token].Login,
                Race = dict[token].Race,
                Token = token
            };
        }

        /// <summary>
        /// Connects a user.
        /// </summary>
        /// <param name="Creds">User credentials.</param>
        /// <returns>The session info created for the user.</returns>
        public static Session ConnectUser(PLICCredentials Creds)
        {
            RefreshSessions();
            if (Authenticate(Creds))
            {
                Session sess = CreateSession(Creds);

                if (RegisterSession(sess))
                    return sess;
                else
                    return SessionRepository[sess.Token];
            }
            else
                return null;
        }

        /// <summary>
        /// Refreshes the timeout value of a session to keep it alive.
        /// </summary>
        /// <param name="token">The session token.</param>
        /// <returns></returns>
        public static bool RefreshSession(string token)
        {
            RefreshSessions();
            if (SessionRepository.ContainsKey(token))
            {
                SessionRepository[token].LastActivity = DateTime.Now.Ticks;
                return true;
            }
            return false;
        }

        /// <summary>
        /// Closes a session if it exists.
        /// </summary>
        /// <param name="token">The session token.</param>
        public static void CloseSession(string token)
        {
            SessionRepository.Remove(token);
        }

        #region PrivateMethods
        private static bool Authenticate(PLICCredentials Creds)
        {
            return _sda.TryLogin(Creds) != null;
        }

        private static bool RegisterSession(Session Session)
        {
            if (SessionRepository.ContainsKey(Session.Token))
                return false;
            SessionRepository.Add(Session.Token, Session);
            return true;
        }

        private static void PresetGameEntitiesId(Session Session)
        {
            Session.ArmyId = _pda.GetPlayerArmyId(Session.Login);
            Session.CityId = _pda.GetPlayerCityId(Session.Login);
            Session.Race = _pda.GetPlayerRace(Session.Login);
        }

        private static Session CreateSession(PLICCredentials Creds)
        {
            Session Sess = new Session()
            {
                Login = Creds.Login,
                Token = CreateToken(Creds)
            };
            PresetGameEntitiesId(Sess);
            return Sess;
        }

        private static string CreateToken(PLICCredentials Creds)
        {
            MD5 hasher = MD5.Create();
            byte[] bytes = hasher.ComputeHash(Encoding.Default.GetBytes(Creds.Login + Creds.Password));
            StringBuilder sb = new StringBuilder();

            for (int i = 0; i < bytes.Length; i++)
            {
                sb.Append(bytes[i].ToString("x2"));
            }
            return sb.ToString();
        }

        /// <summary>
        /// Inner utility function used to update states of the objets
        /// contained inside this class.
        /// </summary>
        private static void RefreshSessions()
        {
            string tmp = "";
            foreach (var pair in SessionRepository)
            {
                if (DateTime.Now.Ticks - pair.Value.LastActivity > SessionLifetime)
                {
                    tmp = pair.Key;
                    break;
                }
            }
            if (tmp != "")
                CloseSession(tmp);
        }
        #endregion

        #region PrivateMembers
        private static SessionDataAccess _sda = new SessionDataAccess();
        private static PlayerDataAccess _pda = new PlayerDataAccess();
        #endregion

    }
}
